import math
import tempfile
import os, sys, platform, torch, time, subprocess, glob, hashlib, base64, tempfile, traceback, threading, shutil, json, random, requests, ctypes # type: ignore
from datetime import datetime
from typing import Literal
from uuid import uuid4
import multiprocessing.shared_memory
from fastapi import WebSocket # type: ignore
from PIL import Image # type: ignore
from io import BytesIO
from starlette.websockets import WebSocketState # type: ignore

def clear__pycache__():
    os.system('find . -type d -name "__pycache__" -exec rm -rf {} +')
    os.system('echo ">> clear__pycache__!"')

def getLogTime():
    return datetime.now().strftime('%m-%d %H:%M:%S')

class logLib():
    def __init__(self, modelName: str = None) -> None:
        self.flush = False
        self.name = modelName
        pass
    
    def showLog(self, msg: any, head='init', type: str = 'debug', logLevel:Literal['debug', 'info', None] = 'info', end=None, flush=None):
        time = getLogTime()
        if logLevel == 'info' and type == 'debug':
            return
        else:
            if self.name is not None:
                head = f'{self.name}.{head}'
            if flush is not None:
                args = {'end':end, 'flush':flush}
                if self.flush != True:
                    self.flush = True
                    try:
                        print(f'{time} {head} {type} ==> {msg}', **args)
                    except:
                        print(f'{time} {head} {type} ==>', msg, **args)
                else:
                    print(msg, **args)
            else:
                self.flush = False
                if msg == '':
                    print('')
                else:
                    print(f'{time} {head} {type} ==>', msg)
log = logLib()
try:
    import redis
except:
    log.showLog('redis lib not install', type="error")
try:
    import sqlite3
except:
    log.showLog('sqlite3 lib not install', type="error")
cacheBasePath = '/dev/shm/aibot'
class MemCache():
    def __init__(self, name: str = "cache") -> None:
        self.name = name
        self.splitstr = '\b'
        self.dir = f'{cacheBasePath}/{name}'
        if os.path.exists(self.dir) == False:
            os.makedirs(self.dir)
            
    def getWorker(self, *args, **kwargs):
        result = []
        id = ''
        key= ''
        keys=None
        dir = self.dir
        if 'id' in kwargs:
            id = kwargs['id']
        if 'key' in kwargs:
            key = kwargs['key']
        if 'keys' in kwargs:
            keys = kwargs['keys']
        if 'all' not in kwargs and 'id' not in kwargs:
            if os.path.exists(dir) == True:
                for filename in os.listdir(dir):
                    # 检查文件是否以.index结尾
                    result.append(filename)
                id = result[generate_random_numbers(0, len(result)-1)]
        
        if 'all' in kwargs:
            items = []
            for s in os.listdir(self.dir):
                item = {}
                item['id'] = s
                dir = f'{self.dir}/{s}'
                if key == '':
                    if os.path.exists(dir) == True:
                        for filename in os.listdir(dir):
                            item[filename]=self.get(id=s, key=filename)
                else:
                    item[key]=self.get(id=s, key=key)
                items.append(item)
            return items
        else:
            item = {}
            if id != '':
                item['id'] = id
                dir = f'{self.dir}/{id}'
                if key == '':
                    if os.path.exists(dir) == True:
                        for filename in os.listdir(dir):
                            if keys is None:
                                item[filename]=self.get(id=id, key=filename)
                            else:
                                if filename in keys:
                                    item[filename]=self.get(id=id, key=filename)
                else:
                    item[key]=self.get(id=id, key=key)
            return item
    
    def get(self, *args, **kwargs):
        result = ''
        dir = self.dir
        if 'id' in kwargs:
            id = kwargs['id']
            dir = f'{self.dir}/{id}'
        if os.path.exists(dir) == False:
            os.makedirs(dir)
        if 'key' in kwargs:
            key = kwargs['key']
            if os.path.exists(f'{dir}/{key}'):
                with open(f'{dir}/{key}', 'r') as f:
                    result = f.read()
        if result != '':
            result = self.type2Data(result)
        return result
    
    def set(self, *args, **kwargs):
        dir = self.dir
        if 'value' in kwargs:
            value = self.data2Type(kwargs['value'])
            id = ''
            key = ''
            expire = 0
            if 'id' in kwargs:
                id = kwargs['id']
                dir = f'{self.dir}/{id}'
            if os.path.exists(dir) == False:
                os.makedirs(dir)
            if 'key' in kwargs:
                key = kwargs['key']
            if 'expire' in kwargs:
                expire = kwargs['expire']
                try:
                    expire = int(expire)
                except:
                    expire = 0
            if value is not None:
                with open(f'{dir}/{key}', 'w') as f:
                    f.write(value)
            
    def gets(self, *args, **kwargs):
        result = {}
        if 'id' in kwargs:
            for key, value in kwargs.items():
                if key != 'id':
                    result[key] = self.get(id=kwargs["id"], key=key)
        return result
    
    def sets(self, *args, **kwargs):
        if 'id' in kwargs:
            for key, value in kwargs.items():
                if key != 'id':
                    self.set(id=kwargs["id"], key=key, value=value)
    
    def getHistory(self, *args, **kwargs):
        result = []
        if 'id' in kwargs:
            dir = f"{self.dir}/history/{kwargs['id']}"
            if os.path.exists(dir) == True:
                with open(f'{dir}', 'r') as f:
                    data = f.read()
                if data != '':
                    data = self.type2Data(data)
                if isinstance(data, str) or isinstance(data, dict):
                    result.append(data)
                elif isinstance(data, list):
                    result = data
        return result

    def setHistory(self, *args, **kwargs):
        if 'id' in kwargs and 'value' in kwargs:
            result = self.getHistory(*args, **kwargs)
            dir = f"{self.dir}/history/"
            if os.path.exists(dir) == False:
                os.makedirs(dir)
            result.append(kwargs['value'])
            data = self.data2Type(result)
            with open(f"{dir}/{kwargs['id']}", 'w') as f:
                f.write(data)
    
    def exists(self, *args, **kwargs):
        if 'id' in kwargs:
            id = kwargs['id']
            dir = f'{self.dir}/{id}'
        if 'key' in kwargs:
            key = kwargs['key']
        return os.path.exists(f'{dir}/{key}')
            
    def remove(self, *args, **kwargs):
        if 'id' in kwargs:
            id = kwargs['id']
            dir = f'{self.dir}/{id}'
        if 'key' in kwargs:
            key = kwargs['key']
        if os.path.exists(f'{dir}/{key}'):
            os.remove(f'{dir}/{key}')
            
    def removeAll(self):
        if os.path.exists(self.dir):
            shutil.rmtree(self.dir)
        
    def kill(self):# 设置过期时间（30天）
        expiration_days = 30
        # 获取当前时间
        now = datetime.datetime.now()
        # 遍历目录
        directory = self.dir
        for filename in os.listdir(directory):
            file_path = os.path.join(directory, filename)
            # 检查文件是否为文件（非目录）
            if os.path.isfile(file_path):
                # 获取文件的最后修改时间
                last_modified_time = datetime.datetime.fromtimestamp(os.path.getmtime(file_path))
                # 计算时间差
                time_difference = now - last_modified_time
                # 如果时间差大于过期时间，删除文件
                if time_difference.days > expiration_days:
                    os.remove(file_path)
                    print(f"Deleted expired file: {file_path}")
    
    def data2Type(self, data):
        result = None
        if data != '' and data != None:
            if isinstance(data, dict) or isinstance(data, list):
                result = json.dumps(data, ensure_ascii=False)
            elif isinstance(data, bytes):
                result = 'no support bytes'
            else:
                result = str(data)
        if data == '':
            result = ''
        return result
    
    def type2Data(self, data: any):
        result = ''
        if (data.startswith('{') and data.endswith('}')) or (data.startswith('[') and data.endswith(']')):
            try:
                result = json.loads(data)
            except:
                result = data
        else:
            try:
                if math.isnan(float(data)) == True:
                    if data.find('.') > -1:
                        result = float(data)
                    else:
                        result = int(data)
            except:
                result = str(data)
        return result
    
class SharedMemoryManager:
    def __init__(self, name, size=2048):
        self.name = name
        if size is not None:
            self.size = size
            self.shm = multiprocessing.shared_memory.SharedMemory(create=True, size=self.size, name=self.name)

    def set(self, data):
        if self.shm is None:
            raise ValueError("Shared memory is not created or attached.")
        if isinstance(data, str):
            data = data.encode()
        if len(data) > self.size:
            raise ValueError("Data size exceeds shared memory size.")
        self.shm.buf[:len(data)] = data

    def get(self):
        if self.shm is None:
            raise ValueError("Shared memory is not created or attached.")
        data = self.shm.buf.tobytes().decode()
        return data

    def remove(self):
        if self.shm is not None:
            self.shm.close()
            self.shm.unlink()

class Sqlite3():
    def __init__(self, dbName: str = ":money:", tableName: str = "work", modelType: str = None, logLevel:Literal['debug', 'info', None] = 'info') -> None:
        self.dbName = dbName
        self.tableName = tableName
        self.logLevel = logLevel
        isCreate = False
        if dbName != ":money:":
            cachePath = f'{getCache()}'
            if os.path.exists(cachePath) == False:
                os.makedirs(cachePath)
            if dbName == 'cache':
                dbName = os.path.join(cacheBasePath, self.dbName + '.db')
            else:
                dbName = os.path.join(cachePath, self.dbName + '.db')
            isCreate = not (os.path.exists(dbName))
            self.conn = sqlite3.connect(dbName, check_same_thread=False)
        else:
            local_data = threading.local()
            def get_connection():
                conn = getattr(local_data, 'conn', None)
                if conn is None:
                    conn = sqlite3.connect('file::memory:?cache=shared', uri=True, check_same_thread=False)
                    local_data.conn = conn
                return conn
            self.conn = get_connection()
        self.cursor = self.conn.cursor()
        self.tableName = tableName
        self.modelType = modelType
        
    def commit(self):
        try:
            if self.conn.in_transaction:
                self.conn.commit()
        except:
            pass
            
    def create_table(self, dbName):
        res = self.conn.cursor() # self.cursor
        if dbName.find('cache') == -1:
            tableNames = ['']
            for table in tableNames:
                if table != "":
                    sql = f"""
                        DROP TABLE IF EXISTS {table}
                    """
                    res.execute(sql)
                    self.commit()
            # status: wait-等待操作指令，cmd-前端发出指令，process-后端正在处理，done-后端处理完成，free-空闲
            sql = f"""
                CREATE TABLE IF NOT EXISTS work (
                    id INTEGER PRIMARY KEY,
                    key TEXT,
                    config TEXT,
                    checkpoint TEXT,
                    devices TEXT,
                    systemDefault TEXT,
                    quantization TEXT,
                    embedding TEXT,
                    status TEXT,
                    function TEXT,
                    request TEXT,
                    response TEXT,
                    timestamp INTEGER
                )
            """
            res.execute(sql)
            self.commit()
            sql = f"""
                CREATE TABLE IF NOT EXISTS history (
                    id INTEGER PRIMARY KEY,
                    function TEXT,
                    modelType TEXT,
                    taskId TEXT,
                    value TEXT,
                    valueType TEXT,
                    timestamp INTEGER
                )
            """
            res.execute(sql)
            self.commit()
        else:
            sql = f"""
                CREATE TABLE IF NOT EXISTS cache (
                    id INTEGER PRIMARY KEY,
                    key TEXT,
                    value TEXT,
                    valueType TEXT,
                    expire INTEGER
                )
            """
            res.execute(sql)
            self.commit()
    
    def getHistory(self, taskId: str, modelType: str):
        res = self.conn.cursor() # self.cursor
        res.execute(f"""
            SELECT value, valueType FROM history WHERE taskId = ? and modelType = ? ORDER BY timestamp
        """, (taskId, modelType))
        datas = res.fetchall()
        result = []
        for data in datas:
            result.append(self.type2Data(data[0], data[1]))
        return result
    
    def setHistory(self, taskId: str, modelType: str, value: any):
        res = self.conn.cursor() # self.cursor
        valueType = 'str'
        if isinstance(value, int):
            value = str(value)
            valueType = 'int'
        elif isinstance(value, float):
            value = str(value)
            valueType = 'float'
        elif isinstance(value, dict):
            value = json.dumps(value, ensure_ascii=False)
            valueType = 'dict'
        res.execute(f"""
            insert into history(taskId, modelType, value, valueType, timestamp) values(?, ?, ?, ?, ?)
        """, (taskId, modelType, value, valueType, int(time.time())))
        self.commit()
        
    def removeHistory(self, taskId: str):
        res = self.conn.cursor() # self.cursor
        res.execute(f"""
            DELETE FROM history WHERE taskId = ?
        """, (taskId,))
        self.commit()
        
    def data2Type(self, data):
        result = None
        if data != '' and data != None:
            if isinstance(data, dict) or isinstance(data, list):
                result = json.dumps(data, ensure_ascii=False)
            elif isinstance(data, bytes):
                result = 'no support bytes'
            else:
                result = str(data)
        return result
    
    def type2Data(self, data: any):
        result = ''
        if (data.startswith('{') and data.endswith('}')) or (data.startswith('[') and data.endswith(']')):
            try:
                result = json.loads(data)
            except:
                result = data
        else:
            try:
                if math.isnan(float(data)) == True:
                    if data.find('.') > -1:
                        result = float(data)
                    else:
                        result = int(data)
            except:
                result = str(data)
        return result
    
    def execute(self, sql):
        res = self.conn.cursor() # self.cursor
        res.execute(sql)
        self.commit()
        return self.conn, res
        
    def set(self, key: str, status:any = None, request:any = None, response:any = None, _function:str = '', forceUpdate:bool = False) -> None:
        res = self.conn.cursor() # self.cursor
        timeStamp = int(time.time())
        tableName = self.tableName
        request = self.data2Type(request)
        response = self.data2Type(response)
        if forceUpdate == True:
            self.remove(key=key, tableName=tableName)
        if status == 'wait':
            response = ''
        res.execute(f"""
            SELECT request, response, status, function, timestamp FROM {tableName} WHERE key = ?
        """, (key,))
        result = res.fetchone()
        if result is None:
            res.execute(f"""
                INSERT INTO {tableName} (key, request, response, status, function, timestamp)
                VALUES (?, ?, ?, ?, ?, ?)
            """, (
                key,
                request,
                response,
                status,
                _function,
                timeStamp
            ))
        else:
            if request is None:
                request = result[0]
            if response is None:
                response = result[1]
            if status == '' or status is None:
                status = result[2]
            if _function == '' or _function is None:
                _function = result[3]
            if request == '' or request is None:
                request = ''
            if response == '' or response is None:
                response = ''
            if status == '' or status is None:
                status = 'free'
            if _function == '' or _function == 'NULL' or _function is None:
                _function = ''
            res.execute(f"""
                UPDATE {tableName} set request = ?, response = ?, status = ?, function = ?, timestamp = ? where key = ?
            """, (
                request,
                response,
                status,
                _function,
                timeStamp,
                key,
            ))
        self.commit()

    def get(self, key: str = None, tableName: str = None, where: str = None) -> any:
        if tableName is None:
            tableName = self.tableName
        res = self.conn.cursor() # self.cursor
        datas = None
        sql = f"""
            SELECT request, response, status, function FROM {tableName}
        """
        if key is not None:
            sql = f"""
                {sql} WHERE key = '{key}'
            """
        if where is not None:
            if sql.find("WHERE") > -1:
                sql = f"""
                    {sql} and {where}'
                """
            else:
                sql = f"""
                    {sql} WHERE {where}'
                """
        res.execute(sql)
        datas = res.fetchone()
        if datas is not None:
            if key is not None:
                request = datas[0]
                response = datas[1]
                status = datas[2]
                _function = datas[3]
                request = self.type2Data(request)
                response = self.type2Data(response)
                result = dict(
                    key=key,
                    request=request,
                    response=response,
                    status=status,
                    _function=_function
                )
                return result
        else:
            return datas
        
    def exists(self, key: str) -> bool:
        tableName = self.tableName
        res = self.conn.cursor() # self.cursor
        res.execute(f"""
            SELECT COUNT(*) FROM {tableName} WHERE key = ?
        """, (key,))
        result = res.fetchone()
        return result[0] > 0

    def remove(self, key: str = None) -> None:
        tableName = self.tableName
        res = self.conn.cursor() # self.cursor
        if key is not None:
            res.execute(f"""
                DELETE FROM {tableName} WHERE key in (?)
            """, (key,))
        else:
            res.execute(f"""
                DELETE FROM {tableName}
            """)
        try:
            self.commit()
        except Exception as ex:
            pass
        
    def setCache(self, key, value, expire):
        timestamp = int(time.time())
        tableName = 'cache'
        res = self.conn.cursor() # self.cursor
        res.execute(f"""
            DELETE FROM {tableName} WHERE expire <= ?
        """, (timestamp,))
        self.commit()
        res.execute(f"""
            SELECT COUNT(*) FROM {tableName} WHERE key = ?
        """, (key,))
        result = res.fetchone()
        value, valueByte, valueType = self.data2Type(value)
        if result[0] > 0:
            res.execute(f"""
                UPDATE {tableName} SET value = ?, valueType = ?, expire = ? WHERE key = ?
            """, (value, valueType, timestamp + expire, key,))
        else:
            res.execute(f"""
                INSERT INTO {tableName} (key, value, valueType, expire) VALUES (?, ?, ?, ?)
            """, (key, value, valueType, timestamp + expire,))
        self.commit()
        
    def getCache(self, key):
        timestamp = int(time.time())
        tableName = 'cache'
        value = None
        res = self.conn.cursor() # self.cursor
        res.execute(f"""
            DELETE FROM {tableName} WHERE expire <= ?
        """, (timestamp,))
        self.commit()
        res.execute(f"""
            SELECT value, valueType FROM {tableName} WHERE key = ?
        """, (key,))
        result = res.fetchone()
        if result is not None:
            value = self.type2Data(result[0], result[1], None)
        return value
    
    def close(self) -> None:
        self.cursor.close()
        self.conn.close()

def getCache(chk = False): # os.getcwd()
    cachePath = f'{cacheBasePath}'
    if os.path.exists(cachePath) == False:
        os.makedirs(cachePath)
    cachePath = f'{cachePath}/cache'
    if chk == True:
        if os.path.exists(cachePath) == False:
            os.makedirs(cachePath)
    return cachePath

# sqlite = Sqlite3(dbName="cache", tableName="cache")
# def initCache():
#     cachePath = f'{getCache()}'
#     if os.path.exists(cachePath):
#         shutil.rmtree(cachePath)
#     os.makedirs(cachePath)
#     sqlite.create_table(dbName='cache')

memcache = MemCache()

class CacheDB():
    def __init__(self,
        host = None,
        port = None,
        password = None,
        db = None,
        socket_timeout=None
    ):
        global log
        if socket_timeout is None:
            socket_timeout = 3
        if host is None:
            host = ''
        else:
            log.showLog('Connect Redis Server...', head='CacheDB.init', type='info', end="", flush=True)
        try:
            self.redis = redis.Redis(
                host=host,
                port=port,
                password=password,
                db=db,
                socket_timeout=socket_timeout
            )
            self.redis.get('test')
            self.isRedis = True
        except Exception as ex:
            if host != '':
                log.showLog('fail, Use Local Memory Cache', head='CacheDB.Exception', type='error', end="", flush=True)
            else:
                log.showLog('Use Local Memory Cache', head='CacheDB.init', type='info', end="", flush=True)
            log.showLog('', type='info')
            self.isRedis = False
            self.redis = {}
        
    def delExpire(self):
        timestamp = int(time.time())
        delItem = []
        for item in self.redis:
            if 'expire' in self.redis[item]:
                if abs(timestamp - int(self.redis[item]['timestamp'])) >= int(self.redis[item]['expire']):
                    delItem.append(item)
        for item in delItem:
            del self.redis[item]
            
    def get(self, key, utf8 = True):
        value = ""
        if self.isRedis == True:
            value = self.redis.get(key)
            if value is not None and utf8 == True:
                value = value.decode('utf-8')
            return value
        else:
            self.delExpire()
            if key in self.redis:
                return self.redis[key]['value']
            return None
        
    def set(self, key: str, value, cacheExpire:int=None):
        if self.isRedis == True:
            self.redis.set(key, value, cacheExpire)
        else:
            if cacheExpire is None:
                self.redis[key] = {
                    'value': value
                }
            else:
                timestamp = int(time.time())
                self.redis[key] = {
                    'value':value,
                    'expire':cacheExpire,
                    'timestamp':timestamp
                }
        
    def keys(self):
        return self.redis.keys()
            
    def expire(self, key, cacheExpire):
        timestamp = int(time.time())
        if self.isRedis == True:
            self.redis.expire(key, cacheExpire)
        else:
            self.delExpire()
            self.redis[key]['expire'] = cacheExpire
            self.redis[key]['timestamp'] = timestamp
        
    def list(self):
        if self.isRedis == True:
            return 'only ram mode!'
        else:
            return self.redis
        
    def delete(self, key):
        if self.isRedis == True:
            self.redis.delete(key)
        else:
            self.delExpire()
            if key in self.redis:
                del self.redis[key]
        
    def ttl(self, key):
        if self.isRedis == True:
            return self.redis.ttl(key)
        else:
            self.delExpire()
            if key in self.redis:
                timestamp = int(time.time())
                ttl = 0
                try:
                    ttl = abs(timestamp - int(self.redis[key]['timestamp']))
                    ttl = int(self.redis[key]['expire']) - ttl
                except:
                    pass
                return ttl
            return 0
        
def catchError(head:str = None):
    exc_type, exc_value, exc_traceback = sys.exc_info()
    # 提取追溯帧
    traceback_frames = traceback.extract_tb(exc_traceback)
    # 获取发生错误的文件的完整路径和行号
    filename, line_number, function_name, source_code = traceback_frames[-1]
    if head is not None:
        if head.strip() != '':
            head = f'{head}.{function_name}'
        else:
            head = function_name
    else:
        head = function_name
    log.showLog(f'{filename},行{line_number}发生错误：{exc_value}', head={head}, type='error')
    
# cacheDB = CacheDB(
#     host='192.168.20.31',
#     port=6379,
#     password='123456yY',
#     db=0
# )
# cacheDB.set('test', 1, 2)
# time.sleep(1)
# print(cacheDB.ttl('test'))
# cache_header = 'utils_cache_header'
# cacheLib = CacheDB()

mediaTypeData = {
    '123':'application/vnd.lotus-1-2-3',
    '3gp':'video/3gpp',
    'aab':'application/x-authoware-bin',
    'aam':'application/x-authoware-map',
    'aas':'application/x-authoware-seg',
    'ai':'application/postscript',
    'aif':'audio/x-aiff',
    'aifc':'audio/x-aiff',
    'aiff':'audio/x-aiff',
    'als':'audio/X-Alpha5',
    'amc':'application/x-mpeg',
    'ani':'application/octet-stream',
    'asc':'text/plain',
    'asd':'application/astound',
    'asf':'video/x-ms-asf',
    'asn':'application/astound',
    'asp':'application/x-asap',
    'asx':'video/x-ms-asf',
    'au':'audio/basic',
    'avb':'application/octet-stream',
    'avi':'video/x-msvideo',
    'awb':'audio/amr-wb',
    'bcpio':'application/x-bcpio',
    'bin':'application/octet-stream',
    'bld':'application/bld',
    'bld2':'application/bld2',
    'bmp':'application/x-MS-bmp',
    'bpk':'application/octet-stream',
    'bz2':'application/x-bzip2',
    'cal':'image/x-cals',
    'ccn':'application/x-cnc',
    'cco':'application/x-cocoa',
    'cdf':'application/x-netcdf',
    'cgi':'magnus-internal/cgi',
    'chat':'application/x-chat',
    'class':'application/octet-stream',
    'clp':'application/x-msclip',
    'cmx':'application/x-cmx',
    'co':'application/x-cult3d-object',
    'cod':'image/cis-cod',
    'cpio':'application/x-cpio',
    'cpt':'application/mac-compactpro',
    'crd':'application/x-mscardfile',
    'csh':'application/x-csh',
    'csm':'chemical/x-csml',
    'csml':'chemical/x-csml',
    'css':'text/css',
    'cur':'application/octet-stream',
    'dcm':'x-lml/x-evm',
    'dcr':'application/x-director',
    'dcx':'image/x-dcx',
    'dhtml':'text/html',
    'dir':'application/x-director',
    'dll':'application/octet-stream',
    'dmg':'application/octet-stream',
    'dms':'application/octet-stream',
    'doc':'application/msword',
    'dot':'application/x-dot',
    'dvi':'application/x-dvi',
    'dwf':'drawing/x-dwf',
    'dwg':'application/x-autocad',
    'dxf':'application/x-autocad',
    'dxr':'application/x-director',
    'ebk':'application/x-expandedbook',
    'emb':'chemical/x-embl-dl-nucleotide',
    'embl':'chemical/x-embl-dl-nucleotide',
    'eps':'application/postscript',
    'eri':'image/x-eri',
    'es':'audio/echospeech',
    'esl':'audio/echospeech',
    'etc':'application/x-earthtime',
    'etx':'text/x-setext',
    'evm':'x-lml/x-evm',
    'evy':'application/x-envoy',
    'exe':'application/octet-stream',
    'fh4':'image/x-freehand',
    'fh5':'image/x-freehand',
    'fhc':'image/x-freehand',
    'fif':'image/fif',
    'fm':'application/x-maker',
    'fpx':'image/x-fpx',
    'fvi':'video/isivideo',
    'gau':'chemical/x-gaussian-input',
    'gca':'application/x-gca-compressed',
    'gdb':'x-lml/x-gdb',
    'gif':'image/gif',
    'gps':'application/x-gps',
    'gtar':'application/x-gtar',
    'gz':'application/x-gzip',
    'hdf':'application/x-hdf',
    'hdm':'text/x-hdml',
    'hdml':'text/x-hdml',
    'hlp':'application/winhlp',
    'hqx':'application/mac-binhex40',
    'htm':'text/html',
    'html':'text/html',
    'hts':'text/html',
    'ice':'x-conference/x-cooltalk',
    'ico':'application/octet-stream',
    'ief':'image/ief',
    'ifm':'image/gif',
    'ifs':'image/ifs',
    'imy':'audio/melody',
    'ins':'application/x-NET-Install',
    'ips':'application/x-ipscript',
    'ipx':'application/x-ipix',
    'it':'audio/x-mod',
    'itz':'audio/x-mod',
    'ivr':'i-world/i-vrml',
    'j2k':'image/j2k',
    'jad':'text/vnd.sun.j2me.app-descriptor',
    'jam':'application/x-jam',
    'jar':'application/java-archive',
    'jnlp':'application/x-java-jnlp-file',
    'jpe':'image/jpeg',
    'jpeg':'image/jpeg',
    'jpg':'image/jpeg',
    'jpz':'image/jpeg',
    'js':'application/x-javascript',
    'jwc':'application/jwc',
    'kjx':'application/x-kjx',
    'lak':'x-lml/x-lak',
    'latex':'application/x-latex',
    'lcc':'application/fastman',
    'lcl':'application/x-digitalloca',
    'lcr':'application/x-digitalloca',
    'lgh':'application/lgh',
    'lha':'application/octet-stream',
    'lml':'x-lml/x-lml',
    'lmlpack':'x-lml/x-lmlpack',
    'lsf':'video/x-ms-asf',
    'lsx':'video/x-ms-asf',
    'lzh':'application/x-lzh',
    'm13':'application/x-msmediaview',
    'm14':'application/x-msmediaview',
    'm15':'audio/x-mod',
    'm3u':'audio/x-mpegurl',
    'm3url':'audio/x-mpegurl',
    'ma1':'audio/ma1',
    'ma2':'audio/ma2',
    'ma3':'audio/ma3',
    'ma5':'audio/ma5',
    'man':'application/x-troff-man',
    'map':'magnus-internal/imagemap',
    'mbd':'application/mbedlet',
    'mct':'application/x-mascot',
    'mdb':'application/x-msaccess',
    'mdz':'audio/x-mod',
    'me':'application/x-troff-me',
    'mel':'text/x-vmel',
    'mi':'application/x-mif',
    'mid':'audio/midi',
    'midi':'audio/midi',
    'mif':'application/x-mif',
    'mil':'image/x-cals',
    'mio':'audio/x-mio',
    'mmf':'application/x-skt-lbs',
    'mng':'video/x-mng',
    'mny':'application/x-msmoney',
    'moc':'application/x-mocha',
    'mocha':'application/x-mocha',
    'mod':'audio/x-mod',
    'mof':'application/x-yumekara',
    'mol':'chemical/x-mdl-molfile',
    'mop':'chemical/x-mopac-input',
    'mov':'video/quicktime',
    'movie':'video/x-sgi-movie',
    'mp2':'audio/x-mpeg',
    'mp3':'audio/mp3',
    'mp4':'video/mp4',
    'mpc':'application/vnd.mpohun.certificate',
    'mpe':'video/mpeg',
    'mpeg':'video/mpeg',
    'mpg':'video/mpeg',
    'mpg4':'video/mp4',
    'mpga':'audio/mpeg',
    'mpn':'application/vnd.mophun.application',
    'mpp':'application/vnd.ms-project',
    'mps':'application/x-mapserver',
    'mrl':'text/x-mrml',
    'mrm':'application/x-mrm',
    'ms':'application/x-troff-ms',
    'mts':'application/metastream',
    'mtx':'application/metastream',
    'mtz':'application/metastream',
    'mzv':'application/metastream',
    'nar':'application/zip',
    'nbmp':'image/nbmp',
    'nc':'application/x-netcdf',
    'ndb':'x-lml/x-ndb',
    'ndwn':'application/ndwn',
    'nif':'application/x-nif',
    'nmz':'application/x-scream',
    'nokia-op-logo':'image/vnd.nok-oplogo-color',
    'npx':'application/x-netfpx',
    'nsnd':'audio/nsnd',
    'nva':'application/x-neva1',
    'oda':'application/oda',
    'oom':'application/x-AtlasMate-Plugin',
    'pac':'audio/x-pac',
    'pae':'audio/x-epac',
    'pan':'application/x-pan',
    'pbm':'image/x-portable-bitmap',
    'pcx':'image/x-pcx',
    'pda':'image/x-pda',
    'pdb':'chemical/x-pdb',
    'pdf':'application/pdf',
    'pfr':'application/font-tdpfr',
    'pgm':'image/x-portable-graymap',
    'pict':'image/x-pict',
    'pm':'application/x-perl',
    'pmd':'application/x-pmd',
    'png':'image/png',
    'pnm':'image/x-portable-anymap',
    'pnz':'image/png',
    'pot':'application/vnd.ms-powerpoint',
    'ppm':'image/x-portable-pixmap',
    'pps':'application/vnd.ms-powerpoint',
    'ppt':'application/vnd.ms-powerpoint',
    'pqf':'application/x-cprplayer',
    'pqi':'application/cprplayer',
    'prc':'application/x-prc',
    'proxy':'application/x-ns-proxy-autoconfig',
    'ps':'application/postscript',
    'ptlk':'application/listenup',
    'pub':'application/x-mspublisher',
    'pvx':'video/x-pv-pvx',
    'qcp':'audio/vnd.qcelp',
    'qt':'video/quicktime',
    'qti':'image/x-quicktime',
    'qtif':'image/x-quicktime',
    'r3t':'text/vnd.rn-realtext3d',
    'ra':'audio/x-pn-realaudio',
    'ram':'audio/x-pn-realaudio',
    'rar':'application/x-rar-compressed',
    'ras':'image/x-cmu-raster',
    'rdf':'application/rdf+xml',
    'rf':'image/vnd.rn-realflash',
    'rgb':'image/x-rgb',
    'rlf':'application/x-richlink',
    'rm':'audio/x-pn-realaudio',
    'rmf':'audio/x-rmf',
    'rmm':'audio/x-pn-realaudio',
    'rmvb':'audio/x-pn-realaudio',
    'rnx':'application/vnd.rn-realplayer',
    'roff':'application/x-troff',
    'rp':'image/vnd.rn-realpix',
    'rpm':'audio/x-pn-realaudio-plugin',
    'rt':'text/vnd.rn-realtext',
    'rte':'x-lml/x-gps',
    'rtf':'application/rtf',
    'rtg':'application/metastream',
    'rtx':'text/richtext',
    'rv':'video/vnd.rn-realvideo',
    'rwc':'application/x-rogerwilco',
    's3m':'audio/x-mod',
    's3z':'audio/x-mod',
    'sca':'application/x-supercard',
    'scd':'application/x-msschedule',
    'sdf':'application/e-score',
    'sea':'application/x-stuffit',
    'sgm':'text/x-sgml',
    'sgml':'text/x-sgml',
    'sh':'application/x-sh',
    'shar':'application/x-shar',
    'shtml':'magnus-internal/parsed-html',
    'shw':'application/presentations',
    'si6':'image/si6',
    'si7':'image/vnd.stiwap.sis',
    'si9':'image/vnd.lgtwap.sis',
    'sis':'application/vnd.symbian.install',
    'sit':'application/x-stuffit',
    'skd':'application/x-Koan',
    'skm':'application/x-Koan',
    'skp':'application/x-Koan',
    'skt':'application/x-Koan',
    'slc':'application/x-salsa',
    'smd':'audio/x-smd',
    'smi':'application/smil',
    'smil':'application/smil',
    'smp':'application/studiom',
    'smz':'audio/x-smd',
    'snd':'audio/basic',
    'spc':'text/x-speech',
    'spl':'application/futuresplash',
    'spr':'application/x-sprite',
    'sprite':'application/x-sprite',
    'spt':'application/x-spt',
    'src':'application/x-wais-source',
    'stk':'application/hyperstudio',
    'stm':'audio/x-mod',
    'sv4cpio':'application/x-sv4cpio',
    'sv4crc':'application/x-sv4crc',
    'svf':'image/vnd',
    'svg':'image/svg-xml',
    'svh':'image/svh',
    'svr':'x-world/x-svr',
    'swf':'application/x-shockwave-flash',
    'swfl':'application/x-shockwave-flash',
    't':'application/x-troff',
    'tad':'application/octet-stream',
    'talk':'text/x-speech',
    'tar':'application/x-tar',
    'taz':'application/x-tar',
    'tbp':'application/x-timbuktu',
    'tbt':'application/x-timbuktu',
    'tcl':'application/x-tcl',
    'tex':'application/x-tex',
    'texi':'application/x-texinfo',
    'texinfo':'application/x-texinfo',
    'tgz':'application/x-tar',
    'thm':'application/vnd.eri.thm',
    'tif':'image/tiff',
    'tiff':'image/tiff',
    'tki':'application/x-tkined',
    'tkined':'application/x-tkined',
    'toc':'application/toc',
    'toy':'image/toy',
    'tr':'application/x-troff',
    'trk':'x-lml/x-gps',
    'trm':'application/x-msterminal',
    'tsi':'audio/tsplayer',
    'tsp':'application/dsptype',
    'tsv':'text/tab-separated-values',
    'tsv':'text/tab-separated-values',
    'ttf':'application/octet-stream',
    'ttz':'application/t-time',
    'txt':'text/plain',
    'ult':'audio/x-mod',
    'ustar':'application/x-ustar',
    'uu':'application/x-uuencode',
    'uue':'application/x-uuencode',
    'vcd':'application/x-cdlink',
    'vcf':'text/x-vcard',
    'vdo':'video/vdo',
    'vib':'audio/vib',
    'viv':'video/vivo',
    'vivo':'video/vivo',
    'vmd':'application/vocaltec-media-desc',
    'vmf':'application/vocaltec-media-file',
    'vmi':'application/x-dreamcast-vms-info',
    'vms':'application/x-dreamcast-vms',
    'vox':'audio/voxware',
    'vqe':'audio/x-twinvq-plugin',
    'vqf':'audio/x-twinvq',
    'vql':'audio/x-twinvq',
    'vre':'x-world/x-vream',
    'vrml':'x-world/x-vrml',
    'vrt':'x-world/x-vrt',
    'vrw':'x-world/x-vream',
    'vts':'workbook/formulaone',
    'wav':'audio/wav',
    'wav':'audio/x-wav',
    'wax':'audio/x-ms-wax',
    'wbmp':'image/vnd.wap.wbmp',
    'web':'application/vnd.xara',
    'wi':'image/wavelet',
    'wis':'application/x-InstallShield',
    'wm':'video/x-ms-wm',
    'wma':'audio/x-ms-wma',
    'wmd':'application/x-ms-wmd',
    'wmf':'application/x-msmetafile',
    'wml':'text/vnd.wap.wml',
    'wmlc':'application/vnd.wap.wmlc',
    'wmls':'text/vnd.wap.wmlscript',
    'wmlsc':'application/vnd.wap.wmlscriptc',
    'wmlscript':'text/vnd.wap.wmlscript',
    'wmv':'audio/x-ms-wmv',
    'wmx':'video/x-ms-wmx',
    'wmz':'application/x-ms-wmz',
    'wpng':'image/x-up-wpng',
    'wpt':'x-lml/x-gps',
    'wri':'application/x-mswrite',
    'wrl':'x-world/x-vrml',
    'wrz':'x-world/x-vrml',
    'ws':'text/vnd.wap.wmlscript',
    'wsc':'application/vnd.wap.wmlscriptc',
    'wv':'video/wavelet',
    'wvx':'video/x-ms-wvx',
    'wxl':'application/x-wxl',
    'x-gzip':'application/x-gzip',
    'xar':'application/vnd.xara',
    'xbm':'image/x-xbitmap',
    'xdm':'application/x-xdma',
    'xdma':'application/x-xdma',
    'xdw':'application/vnd.fujixerox.docuworks',
    'xht':'application/xhtml+xml',
    'xhtm':'application/xhtml+xml',
    'xhtml':'application/xhtml+xml',
    'xla':'application/vnd.ms-excel',
    'xlc':'application/vnd.ms-excel',
    'xll':'application/x-excel',
    'xlm':'application/vnd.ms-excel',
    'xls':'application/vnd.ms-excel',
    'xlt':'application/vnd.ms-excel',
    'xlw':'application/vnd.ms-excel',
    'xm':'audio/x-mod',
    'xml':'text/xml',
    'xmz':'audio/x-mod',
    'xpi':'application/x-xpinstall',
    'xpm':'image/x-xpixmap',
    'xsit':'text/xml',
    'xsl':'text/xml',
    'xul':'text/xul',
    'xwd':'image/x-xwindowdump',
    'xyz':'chemical/x-pdb',
    'yz1':'application/x-yz1',
    'z':'application/x-compress',
    'zac':'application/x-zaurus-zac',
    'zip':'application/zip'
}
def iswin():
    return platform.platform().lower().startswith('linux') == False

def uuid():
    return str(uuid4())

def is_file_like_data(data):
    if type(data) != bytes:
        return False
    # 创建一个临时文件
    with tempfile.NamedTemporaryFile(delete=False) as temp_file:
        # 写入字节数据到临时文件
        temp_file.write(data)
        # 检查临时文件是否成功创建
        if temp_file.name and os.path.isfile(temp_file.name):
            # 删除临时文件
            os.remove(temp_file.name)
            return True
    return False

def timeStamp():
    return int(time.time() * 1000)

async def send_message(message: any, websocket: WebSocket):
    if 1==1:
    # try:
        if isinstance(message, str):
            await websocket.send_text(message)
        elif isinstance(message, dict):
            await websocket.send_text(json.dumps(message, ensure_ascii=False))
        else:
            await websocket.send_bytes(message)
    # except:
    #     pass
    
async def taskEnd(task, noClose:bool = False):
    if 'websocket' in task:
        if 'noClose' in task:
            noClose = task['noClose'] == True
        if task['websocket'].client_state != WebSocketState.DISCONNECTED:
            try:
                if 'data' in task:
                    if isinstance(task['data'], str):
                        data = {
                            'data': task['data']
                        }
                        if 'useTime' in task:
                            data['useTime'] = task['useTime']
                        if 'code' in task:
                            data['code'] = task['code']
                        if 'taskId' in task:
                            data['taskId'] = task['taskId']
                        if 'spker' in task:
                            data['spker'] = task['spker']
                        await send_message(data, task['websocket'])
                    else:
                        await send_message(task['data'], task['websocket'])
                    time.sleep(0.3)
                if noClose == False:
                    await task['websocket'].close()
            except Exception as ex:
                print(ex)
                pass
        
def file2Dir(filePath: str):
    tmp = filePath.split('/')
    path = ''
    for i in range(len(tmp) - 2):
        if i > 0:
            path = f'{path}/'
        path = f'{path}{tmp[i]}'
    return path

def media2type(ext:str):
    ext = ext.lower().strip()
    if ext in mediaTypeData:
        return mediaTypeData[ext]
    else:
        return 'application/octet-stream'
    
def ext2media(media:str):
    media = media.lower().strip()
    media = media.replace('data:', '')
    for ext in mediaTypeData:
        if mediaTypeData[ext] == media:
            if ext == 'jpe':
                ext = 'jpg'
            return ext
    return 'tmp'

def generate_random_numbers(start:int = 10000, end: int = 99999, length: int = 1):
    return [random.randint(start, end) for _ in range(length)][0]

def b64Encode(data, ext:str = None):
    # base64编码 data: str, int, byte, filePath
    result = ''
    if ext is not None:
        result = f'data:{media2type(ext)};base64,'
    if type(data) == int or type(data) == float:
        data = str(data)
    if is_file_like_data(data) == True:
        return f"{result}{base64.b64encode(data).decode('utf-8')}"
    else:
        if os.path.isfile(data) == True:
            tmp = data.split('.')
            if ext is None:
                ext = tmp[len(tmp) - 2]
                result = f'data:{media2type(ext)};base64,'
            # 文件数据的 Base64 编码
            with open(data, 'rb') as file:
                return f"{result}{base64.b64encode(file.read()).decode('utf-8')}"
        else:
            return base64.b64encode(data.encode('utf-8')).decode('utf-8')

def b64Decode(data, path = None):
    try:
        if path is None:
            if data.find(';base64,') > -1:
                tmp = data.split(';base64,')
                data = tmp[len(tmp) - 1]
            return base64.b64decode(data).decode('utf-8')
        else:
            ext = ''
            if data.find(';base64,') > -1:
                tmp = data.split(';base64,')
                ext = ext2media(tmp[0])
                data = tmp[len(tmp) - 1]
                # 定义保存文件的路径
                if os.path.exists(path) == False:
                    os.makedirs(path)
                file_path = f"{path}/b64decode_{toMd5(data)}.{ext}"
                if os.path.exists(file_path) == False:
                    # 打开文件并写入上传文件的内容
                    with open(file_path, "wb") as f:
                        f.write(base64.b64decode(data))
                return file_path
    except Exception as ex:
        log.showLog(head='b64Decode', msg=f'b64Encode error ==>{str(ex)}', type='error')

def toMd5(data, is16: bool = True):
    try:
        md5_hash = hashlib.md5()
        if is_file_like_data(data) == True:
            md5_hash.update(data)
        else:
            if os.path.isfile(data) == True:
                # 打开文件
                with open(data, 'rb') as file:
                    # 读取文件内容
                    chunk = 0
                    while chunk != b'':
                        chunk = file.read(8192)  # 读取 8192 字节（8KB）
                        md5_hash.update(chunk)
            else:
                if type(data) != str:
                    if math.isnan(data) == False:
                        data = str(data)
                md5_hash.update(data.encode())
        result = md5_hash.hexdigest()
        if is16 == True:
            return result[8:24]
        return result
        pass
    except Exception as ex:
        log.showLog(head='toMd5', msg=f'b64Encode error ==>{str(ex)}', type='error')
    
def clear():
    os_name = platform.system()
    clear_command = 'cls' if os_name == 'Windows' else 'clear'
    os.system(clear_command)
    pass

def gc():
    if torch.cuda.is_available():
        torch.cuda.empty_cache()
        
def clearCache(cachedir, _type=''):
    cachePath = os.path.join(cachedir, _type)
    if os.path.exists(cachePath):
        isClear = True
        dateStr = str(datetime.now().strftime('%Y-%m-%d'))
        if os.path.exists(f'{cachePath}/timestamp.txt') == False:
            isClear = False
        else:
            result = ''
            with open(f'{cachePath}/timestamp.txt', 'r') as f:
                result = f.read()
            isClear = dateStr == result
        if isClear == False:
            for dir in [d for d in os.listdir(cachePath) if os.path.isdir(os.path.join(cachePath, d))]:
                if dir != dateStr:
                    shutil.rmtree(os.path.join(cachePath, dir))
            with open(f'{cachePath}/timestamp.txt', "w", encoding='utf-8') as f:
                f.write(dateStr)

def chkLicense():
    licenseFile = glob.glob(f'{os.getcwd()}/lib/libLicense.*')
    if len(licenseFile) == 0:
        exit()
    else:
        if licenseFile[0].endswith('.so'):
            from lib.libLicense import license # type: ignore
            try:
                from lib.libVerifyMachineCode import verifyMachineCode # type: ignore
                verifyMachineCode()
            except:
                pass
            localinfo = license()
            localinfo.register()
        pass

def gpusInfo():
    cacheData = memcache.get(id=f'gpusInfo', key='data')
    if cacheData is not None:
        return cacheData['device'], cacheData['list']
    else:
        data = ''
        device = 'CUDA'
        list = []
        if hasattr(torch, 'is_mlu_available') and torch.is_mlu_available():
            device = 'MLU'
        if device == 'MLU':
            data = subprocess.run(['cnmon'], stdout=subprocess.PIPE)
            data = str(data.stdout).strip()
            data = data.replace("b'", "")
            idx = 0
            splitstr = '+-------------------------------+----------------------+-----------------------+'
            splitstr2 = '===============================+======================+======================='
            data=data.split(splitstr)
            for d in data:
                if (idx > 0 and idx < len(data) - 1):
                    tmp = []
                    if d.find(splitstr2) > -1:
                        d = d.split(splitstr2)[1]
                    while d.find('   ') > -1:
                        d = d.replace('   ', '  ')
                    d = d.replace('\\n|', '').replace('|\\n', '')
                    if d.startswith('|'):
                        d = d[1:]
                    d = d.replace('%', '').replace('C', '').replace('W', '')
                    d = d.strip().split('\\n|')
                    for sd in d:
                        sd = sd.split('|')
                        for ssd in sd:
                            tmp.append(ssd.strip())
                    names = tmp[0].split('/')
                    vram = tmp[4].strip().replace('MiB', '').split('/')
                    power = tmp[3].strip().split('  ')
                    fan = power[0].replace('%', '').strip()
                    tempture = power[1].replace('C', '').strip()
                    power = power[2].replace('W', '').strip().split('/')
                    list.append({
                        'index': int(names[0].strip()),
                        'name': names[1].strip().split(' ')[0],
                        'gpuBusId': tmp[1].strip(),
                        'gpuUtil': int(tmp[2].strip().split(' ')[0].strip()),
                        'vram': [int(vram[0].strip()), int(vram[1].strip())],
                        'powerDraw': [int(power[0].strip()), int(power[1].strip())],
                        'tempture': float(tempture),
                        'fan': int(fan)
                    })
                idx += 1
        else:
            data = subprocess.run(['nvidia-smi', '--format=csv,noheader,nounits', '--query-gpu=index,gpu_name,gpu_bus_id,utilization.gpu,memory.used,memory.total,power.draw,power.default_limit,temperature.gpu,fan.speed,power.max_limit'], stdout=subprocess.PIPE)
            data = str(data.stdout).strip()
            data = data.replace("b'", "")
            for d in data.split('\n'):
                if d.endswith("'"):
                    d = d[:-1]
                ds = []
                for sd in d.split(','):
                    sd = sd.strip()
                    try:
                        sd = int(sd)
                    except:
                        try:
                            sd = float(sd)
                        except:
                            pass
                    ds.append(sd)
                list.append({
                    'index': ds[0],
                    'name': ds[1],
                    'gpuBusId': ds[2],
                    'gpuUtil': ds[3],
                    'vram': [ds[4], ds[5]],
                    'powerDraw': [ds[6], ds[7]],
                    'tempture': ds[8],
                    'fan': ds[9]
                })
    memcache.set(id=f'gpusInfo', key='data', value = {'device': device, 'list': list})
    return device, list

# subProcess
def getGPUs():
    device = 'CUDA'
    if hasattr(torch, 'is_mlu_available') and torch.is_mlu_available():
        device = 'MLU'
    if device == 'MLU':
        result = subprocess.run(['cnmon', '-l'], stdout=subprocess.PIPE)
    else:
        result = subprocess.run(['nvidia-smi', '--query-gpu=index', '--format=csv,noheader'], stdout=subprocess.PIPE)
    # 解析输出以获取GPU数量
    num_gpus = len(result.stdout.strip().split(b'\n'))
    return num_gpus

def getParams(Object: dict, key: str, default:any = ''):
    try:
        if key.find(".") == -1:
            if key in Object:
                return Object[key]
            else:
                return default
        else:
            keys = key.split('.')
            obj = Object
            for k in keys:
                if k in obj:
                    obj = obj[k]
                else:
                    return default
            return obj
    except:
        return default
    
def isNaN(value: any) -> bool:
    result = False
    try:
        temp = int(value) / 1
        if isinstance(temp, int):
            result = False
        else:
            result = True
    except:
        result = True
    return result

def chkinput(data):
    if data is not None:
        hr_scale = getParams(data, 'hr_scale', None)
        enable_hr = hr_scale != None
        n_iter = getParams(data, 'n_iter', 1)
        if isNaN(n_iter) == True:
            n_iter = 1
        if n_iter < 1:
            n_iter = 1
        if n_iter > 4:
            n_iter = 4
        clip_skip = getParams(data, 'clip_skip', 1)
        if isNaN(clip_skip) == True:
            clip_skip = 1
        if clip_skip < 1:
            clip_skip = 1
        if clip_skip > 12:
            clip_skip = 12
        steps = getParams(data, 'steps', 20)
        if isNaN(steps) == True:
            steps = 20
        if steps < 1:
            steps = 1
        if steps > 50:
            steps = 50
        cfg_scale = getParams(data, 'cfg_scale', 7)
        if isNaN(cfg_scale) == True:
            cfg_scale = 7
        if cfg_scale < 1:
            cfg_scale = 1
        if cfg_scale > 20:
            cfg_scale = 20
        width = getParams(data, 'width', 512)
        if isNaN(width) == True:
            width = 512
        if width < 64:
            width = 64
        if width > 1024:
            width = 1024
        height = getParams(data, 'height', 512)
        if isNaN(height) == True:
            height = 512
        if height < 64:
            height = 64
        if height > 1024:
            height = 1024
        if hr_scale is not None:
            if isNaN(hr_scale) == True:
                hr_scale = 1
            if hr_scale < 1:
                hr_scale = 1
            if hr_scale > 4:
                hr_scale = 4
        data['override_settings'] = dict(
            sd_model_checkpoint = getParams(data, "override_settings.sd_model_checkpoint", "v1-5-pruned-emaonly.safetensors"),
            # 采样器模型
            sd_vae = getParams(data, "override_settings.sd_vae", "Automatic"),
            # 色彩模式 (VAE)
        )
        data['prompt'] = getParams(data, 'prompt')
        # 提示词
        data['negative_prompt'] = getParams(data, 'negative_prompt')
        # 反向提示词
        data['cfg_scale'] = cfg_scale
        # 提示词相关性(提示词強度)
        data['steps'] = steps
        # 迭代步数
        data['width'] = getParams(data, 'width', 512)
        # 生成图像宽度
        data['height'] = getParams(data, 'height', 512)
        # 生成图像高度
        data['n_iter'] = n_iter
        # 生成图像数量
        data['enable_hr'] = enable_hr
        # 高分辨率修复（启用与hr_scale合用）
        data['hr_scale'] = hr_scale
        # 高分辨率修复 倍数 1x 2x 3x 4x
        data['Seed'] = getParams(data, 'Seed', -1)
        # 随机种子
        data['clip_skip'] = clip_skip
        # CLIP终止层数 (1-12)
        if 'init_images' in data:
            if isinstance(data['init_images'], list):
                newImages = []
                for img in data['init_images']:
                    if img.startswith('http://') or img.startswith('https://'):
                        res =  requests.get(img)
                        if res.status_code == 200:
                            newImages.append(res.content)
                    elif img.find('base64,') > -1:
                        image_data = base64.b64decode(img.split('base64,')[1])
                        # 将图像数据转换为PIL图像对象
                        image = Image.open(BytesIO(image_data))
                        newImages.append(image)
                    else:
                        if os.path.exists(img):
                            with open(img, 'rb') as f:
                                newImages.append(f.read())
            else:
                data['init_images'] = []
    return data

async def makeRequest(models, cmd, request, getRequestItem, timeout=None, noCache=False, JSONResponse=None):
    code = 200
    result = None
    if noCache == False:
        result = memcache.get(id='sd', key=toMd5(request.url.path))
    if result is None or result == '':
        data = await getRequestItem(request)
        work_id = ''
        if 'work_id' in data:
            work_id = data['work_id']
            if len(data.keys()) == 1:
                data = None
            else:
                del data['work_id']
        pars = dict(
            cmd=cmd,
            id=work_id,
        )
        if data is not None:
            pars.update(data=data)
        if 'StableDiffusion' in models:
            code, result = models['StableDiffusion']['works'].sdExec(**pars)
            if 'list' in result:
                result = result['list']
            if timeout is not None and code == 200 and noCache == False:
                memcache.set(id='sd', key=toMd5(request.url.path), value = result, expire = timeout * 30)
        else:
            code = 404
            result = 'Model not started'
    if JSONResponse is not None:
        return JSONResponse(
            content={'code':code,'data':result},
            status_code=code,
            headers={"x-token": "boatToken"},
            media_type="application/json" #"text/html"
        )
    else:
        return code, result
    
def kill_thread(inner_work_thread):
    if not inner_work_thread.is_alive():
        return
    # 终止线程
    async_exc = ctypes.py_object(SystemExit)
    ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(inner_work_thread.ident), async_exc)